# prediction tool
import os
import pickle
import pandas as pd
import numpy as np
from concurrent.futures import ProcessPoolExecutor, wait
from utils_func.env import ROOT_PATH
from constant import methods,universes
from utils import check_memory, show_time, check_data, check_tofill, get_month_range, add_new_cols, rearrange_df, process_dataframe
from savetosql import savetosql
from utils_func.get_logger import get_logger

logger = get_logger("monitor", ROOT_PATH.joinpath('log', 'monitor.log'))
def risk_allocate(start_day,end_day,fee,method='时间均摊法'):
    month_list = get_month_range(start_day,end_day)
    n = len(month_list)
    fee_allocation = []
    if method == '时间逆总和法':
        for i in range(n):
            fee_allocation.append( (i+1)*fee / ((1+n)*n/2) )
    elif method == '时间总和法':
        for i in range(n):
            fee_allocation.append( (n-i)*fee / ((1+n)*n/2) )
    else: fee_allocation = [fee/n] * n
    return month_list,fee_allocation

if __name__ == '__main__':
    logger.info('START')
    # 输入批次月份起始, 左闭右开
    start_mon = input("请输入统计开始的批次月份,形如200801,表示2008年01月):")
    end_mon = input("请输入统计开始的批次月份,形如202001,表示2020年01月):")
    # 输入统计项目
    universe_id = input("请选择统计项目(a:苏宁大陆华泰,b:苏宁香港人保,c:苏宁大陆人保,d:联想人保):")# TODO 可根据需求添加universe
    universe = universes[universe_id]['cn_name']#'苏宁大陆人保'

    # 创建结果项目路径
    res_path = os.path.join(ROOT_PATH,f'result\\{universe}')
    if not os.path.exists(res_path):
        os.makedirs(res_path)
    
    # 数据读取:净费规则表,保费分摊表,理赔原始数据
    net_premium = pd.read_excel(f'{ROOT_PATH}\constant\净费规则表_{universe}.xlsx')#
    gross_premium_raw = pd.read_csv(f'{ROOT_PATH}\data\\1_保费分摊表_{universe}.csv',encoding='GBK')#_无批次月份
    # lp_raw = pd.read_csv(f'{ROOT_PATH}\data\\2_理赔原始数据_{universe}.csv',encoding='GBK')#_无批次月份
    # lp_raw['维修申请时间'] = pd.to_datetime(lp_raw['维修申请时间']).dt.strftime('%Y-%m-%d')

    # 数据检查
    gross_premium, gross_premium_abnormal = check_data(gross_premium_raw)
    # lp, lp_abnormal = check_data(lp_raw) #TODO save to revise abnormal values

    # 净费匹配
    gross_premium['匹配用'] = gross_premium['项目']+gross_premium['保司']+gross_premium['统计品类']+gross_premium['产品简称']
    right_df = net_premium[['净费','匹配用','产品层级2']]
    df_tocheck = gross_premium.join(right_df.set_index('匹配用'),on='匹配用')

    df_tocheck['批次月份'] = df_tocheck['批次月份'].apply(str)
    pici = df_tocheck['批次月份'].unique()
    range_yr = pici[(pici>=start_mon) == (pici<end_mon)]
    df_tocheck = df_tocheck.loc[df_tocheck['批次月份'].isin(range_yr)]
    # df_tocheck.loc[~df_tocheck['批次月份'].isin(range_yr)]]
    
    # 储存待补充净费表
    check_item = '净费'
    df_tofill = df_tocheck.loc[df_tocheck[check_item].isnull()]#净费为空值
    cols = list(set(df_tofill.columns).intersection(set(net_premium.columns)))
    df_checked = check_tofill(df_tofill)[cols]
    if not df_checked.empty:
        df_checked.to_excel(f'{res_path}\\待补充{check_item}.xlsx',index=False)
        logger.info('存在产品净费确实须补充！')
    df = df_tocheck.dropna(subset=[check_item])

    # 时间戳处理为datetime形式
    df.loc[:,'保障开始时间'] = pd.to_datetime(df['保障开始时间'])
    df.loc[:,'保障结束时间'] = pd.to_datetime(df['保障结束时间'])
    df.dropna(how='all',axis=1,inplace=True)#去掉全空值列
    # # if universe == '苏宁大陆人保':

    criterion = ''
    # 统计filtering
    filter_list = ['产品层级2','统计品类']  # TODO 可根据需求添加filter
    df_filtered = df.copy(deep=True)
    for _filter in filter_list:
        all_filter_vals = list(df[_filter].unique())
        logger.info(f'{universe}项目下{_filter}有以下:{all_filter_vals}')
        # 输入统计所需的filter值
        filter_vals = input(f"请以列表形式输入{_filter}({all_filter_vals}),否则回车统计覆盖项目所有:")  #还有一类有nan怎么处理？
        
        if filter_vals != '':
            criterion += _filter + ':' + filter_vals + ' '
            df_filtered = df_filtered[df_filtered[_filter].isin(filter_vals)]
        else:
            criterion += _filter + ':' + 'all' + ' '

    # 输入风险分摊方法
    method_id = input("请选择风险分摊方法序号(a.时间均摊法 b.时间总和法 c.时间逆总和法):")# TODO 可根据需求添加method
    method = methods[method_id]['cn_name']
    method_abbr = methods[method_id]['en_abbr']
    criterion += '风险分摊方法' + ':' + method

    # 创建分摊方式结果子目录
    res_subpath = os.path.join(res_path,f'{method}')
    if not os.path.exists(res_subpath):
        os.makedirs(res_subpath)

    # 存储过滤筛选条件criterion
    file = open(f'{res_subpath}\\统计条件.txt', 'w', encoding='Utf-8')
    a = file.write(criterion)
    file.close()

    logger.info(f'按批次月份参与计算的数据{start_mon}_{end_mon}(区间左闭右开)条数为:{df_filtered.shape[0]}') 
    df_baofei = df_filtered.copy(deep=True)
    df_jingfei = df_filtered.copy(deep=True)

    toallocate_dict = {'BAOFEI':df_baofei,'JINGFEI':df_jingfei}#TODO 对苏宁大陆人保项目来说, BAOFEI&JINGFEI08-19可以一起跑,20-24数据量太大BAOFEI&JINGFEI需要分开跑
    allocation_dict = {}

    for key,v_df in toallocate_dict.items():
        # 分摊
        show_time(f'{key}分摊计算开始')
        if key == 'BAOFEI':
            v_df[['month_range','fee_allocation']] = v_df.apply(lambda x: risk_allocate(x['保障开始时间'],x['保障结束时间'],x['保费总和'],method), axis=1, result_type='expand')#*x['主承保共保比例']
        if key == 'JINGFEI':
            v_df[['month_range','fee_allocation']] = v_df.apply(lambda x: risk_allocate(x['保障开始时间'],x['保障结束时间'],x['净费']*x['单量'],method), axis=1, result_type='expand')#*x['主承保共保比例']
        show_time(f'{key}分摊计算结束')

        show_time(f'{key}画图数据准备开始')
        allocation_df = add_new_cols(v_df,'month_range','fee_allocation')#画图用
        # allocation_dict[key] = allocation_df
        show_time(f'{key}画图数据准备结束')
        allocation_df.to_csv(os.path.join(res_subpath,f'{key}_{start_mon}-{end_mon}.csv'))

        if universe != '苏宁大陆人保':
            show_time(f'{key}数据展开start')
            res_df = process_dataframe(v_df,'month_range','fee_allocation')
            show_time(f'{key}数据展开end')

            show_time(f'存储{key}-SQL库表开始')
            table_name = f'ALLOC_{key}_{universes[universe_id]["en_name"]}_{method_abbr}_{start_mon[2:4]}_{end_mon[2:4]}'
            savetosql(res_df,table_name)
            show_time(f'存储{key}-SQL库表结束')

        else:
            # partitions = [int(.2*len(v_df)), int(.4*len(v_df)), int(.6*len(v_df)), int(.8*len(v_df))]
            partitions = [int(perct*len(v_df)) for perct in np.arange(0,1,0.1)[1:]]
            slice_list = np.split(v_df, partitions)
            for i in range(len(slice_list)):#[:2]
            # i=0
                slice = slice_list[i]#.head(5000)
                logger.info(f'参与计算的切片{i}数据量:{slice.shape[0]}') 
                show_time(f'{key}数据展开start')
                res_slice = process_dataframe(slice,'month_range','fee_allocation')
                show_time(f'{key}数据展开end')
                show_time(f'{key}-SQL存储开始')
                table_name = f'ALLOC_{key}_{universes[universe_id]["en_name"]}_{method_abbr}_{start_mon[2:4]}_{end_mon[2:4]}'
                savetosql(res_slice,table_name)
                show_time(f'{key}-SQL存储完毕')

    logger.info('END')
